library(tidyverse)
library(langcog)
library(psych)
library(readxl)
library(cowplot)
library(lme4)
library(lmerTest)
library(kableExtra)
library(lubridate)
library(ggdendro)
theme_set(theme_bw())
heatmap_fun <- function(efa, factor_names = NA){
  
  # get factor names
  if(is.na(factor_names)){
    factor_names <- paste("Factor", 1:efa$factors)
  }
  
  # put factors in a standard order when applicable
  body_factors <- factor_names[grepl("BODY", factor_names)]
  
  leftovers <- factor_names[!factor_names %in% body_factors]
  heart_factors <- leftovers[grepl("HEART", leftovers)]
  
  leftovers <- leftovers[!leftovers %in% heart_factors]
  mind_factors <- leftovers[grepl("MIND", leftovers)]
  
  other_factors <- leftovers[!leftovers %in% mind_factors]
  
  factor_levels <- c(body_factors, heart_factors, mind_factors, other_factors)
  
  # get factor loadings
  loadings <- efa$loadings[] %>%
    data.frame() %>%
    rownames_to_column("capacity") %>%
    gather(factor, loading, -capacity) %>%
    mutate(factor = as.character(factor(factor, labels = factor_names)),
           factor = factor(factor, levels = factor_levels))
  
  # get fa.sort() order
  order <- loadings %>%
    group_by(capacity) %>%
    top_n(1, abs(loading)) %>%
    ungroup() %>%
    arrange(desc(factor), abs(loading)) %>%
    mutate(order = 1:length(levels(factor(loadings$capacity)))) %>%
    select(capacity, order)
  
  # get percent shared variance explained
  shared_var <- efa$Vaccounted %>%
    data.frame() %>%
    rownames_to_column("stat") %>%
    filter(stat == "Proportion Explained") %>%
    select(-stat) %>%
    gather(factor, var) %>%
    mutate(factor = as.character(factor(factor, labels = factor_names)),
           factor = factor(factor, levels = factor_levels)) %>%
    mutate(var_shared = paste0(factor, "\n", round(var, 2)*100, "% shared var.,"))
  
  # get percent total variance explained
  total_var <- efa$Vaccounted %>%
    data.frame() %>%
    rownames_to_column("stat") %>%
    filter(stat == "Proportion Var") %>%
    select(-stat) %>%
    gather(factor, var) %>%
    mutate(factor = as.character(factor(factor, labels = factor_names)),
           factor = factor(factor, levels = factor_levels)) %>%
    mutate(var_total = paste0(round(var, 2)*100, "% total var."))
  
  # make plot
  plot <- ggplot(loadings %>% 
                   left_join(order) %>%
                   left_join(shared_var %>% select(-var)) %>%
                   left_join(total_var %>% select(-var)) %>%
                   mutate(capacity = gsub("_", " ", capacity),
                          factor = factor(factor, levels = factor_levels),
                          xlab = paste(var_shared, var_total, sep = "\n")),
                 aes(x = reorder(xlab, as.numeric(factor)), 
                     y = reorder(capacity, order), 
                     fill = loading, 
                     label = format(round(loading, 2), nsmall = 2))) +
    geom_tile(color = "black") +
    geom_text(size = 3) +
    scale_fill_distiller(limits = c(-1, 1), 
                         palette = "RdYlBu",
                         guide = guide_colorbar(barheight = 10)) +
    theme_minimal() +
    scale_x_discrete(position = "top") +
    theme(axis.title = element_blank())
  
  return(plot)
  
}
s_moments <- function(p) {p*(p+1)/2}
param_est <- function(p, k) {p*k + p - (k*(k-1)/2)}
check_ok <- function(p, k) {
  a <- (p-k)^2
  b <- p+k
  return(ifelse(a>b, TRUE, FALSE))
}
max_ok <- function(p) {
  df_check <- data.frame()
  for(i in 1:p){
    df_check[i,"check"] <- check_ok(p,i)
  }
  max <- df_check %>% filter(check) %>% nrow()
  return(max)
}
reten_fun <- function(df, rot_type = c("oblimin", "varimax", "none")){
  
  # figure out max number of factors to retain
  n_var <- length(names(df))
  max_k <- max_ok(n_var)
  
  # run efa with max factors, unrotated
  fa_unrot <- fa(df, nfactors = max_k, rotate = "none", 
                 scores = "tenBerge", impute = "median")
  eigen <- fa_unrot$Vaccounted %>%
    data.frame() %>%
    rownames_to_column("param") %>%
    gather(factor, value, -param) %>%
    spread(param, value) %>%
    filter(`SS loadings` > 1, `Proportion Explained` > 0.05)
  retain_k <- nrow(eigen)
  
  fa_rot <- fa(df, nfactors = retain_k, rotate = rot_type,
               scores = "tenBerge", impute = "median")
  
  loadings <- fa_rot$loadings[] %>%
    data.frame() %>%
    rownames_to_column("capacity") %>%
    gather(factor, loading, -capacity) %>%
    group_by(capacity) %>%
    top_n(1, abs(loading)) %>%
    ungroup() %>%
    count(factor)
  retain_k_final <- nrow(loadings)
  
  return(retain_k_final)
}
source("./scripts/p7_data_prep.R")
Joining, by = c("p7_entr", "p7_2day", "p7_ver", "p7_batc", "p7_resample", "p7_ctry", "p7_subj", "p7_file", "p7_recr", "p7_wher", "p7_whoc", "p7_abs_child.exp", "p7_abs_poetic", "p7_abs_tv.real", "p7_abs_see.image", "p7_abs_mind.world", "p7_abs_clouds", "p7_abs_vivid.dreams", "p7_abs_mystic.exp", "p7_abs_step.outside", "p7_abs_textures", "p7_abs_too.real", "p7_abs_music.attn", "p7_abs_heavy.body", "p7_abs_sense.presc", "p7_abs_fire", "p7_abs_nature.art", "p7_abs_colors", "p7_abs_thght.wander", "p7_abs_vivid.past", "p7_abs_makes.sense", "p7_abs_become.chctr", "p7_abs_visual.thghts", "p7_abs_small.things", "p7_abs_music.lift", "p7_abs_noise.music", "p7_abs_scented.mem", "p7_abs_visual.music", "p7_abs_before.said", "p7_abs_physical.mem", "p7_abs_voice.sound", "p7_abs_not.physical", "p7_abs_thgts.image", "p7_abs_odor.to.color", "p7_abs_sunset", "p7_abs_total", "p7_abs_check", "p7_dse_god.prescn", "p7_dse_conect.life", "p7_dse_no.daily.conc", "p7_dse_spi.strength", "p7_dse_spirt.comfort", "p7_dse_inner.peace", "p7_dse_god.help", "p7_dse_guided.daily", "p7_dse_direct.love", "p7_dse_lov.thru.othr", "p7_dse_touch.by.beau", "p7_dse_blessings", "p7_dse_selfless.care", "p7_dse_accept.wrong", "p7_dse_total", "p7_dse_check", "p7_se_voice.out", "p7_se_voice.in", "p7_se_placed.thought", "p7_se_vision.out", "p7_se_image.in", "p7_se_touch", "p7_se_smell", "p7_se_taste", "p7_se_dream.sent", "p7_se_stand.beside", "p7_se_demon.in.room", "p7_se_spnat.presence", "p7_se_shaking.prayer", "p7_se_emotion.prayer", "p7_se_powrful.prayer", "p7_se_out.body.exp", "p7_se_body.control", "p7_se_slep.paralysis", "p7_se_god.thru.pain", "p7_se_god.illness", "p7_se_live.healing", "p7_se_own.healing", "p7_se_total", "p7_se_check", "p7_wob_set.mind_reverse", "p7_wob_find.ways_reverse", "p7_wob_own.hands_reverse", "p7_wob_future.on.me_reverse", "p7_wob_little.change", "p7_wob_helpless", "p7_wob_others.do", "p7_wob_beynd.control", "p7_wob_interfere", "p7_wob_little.contrl", "p7_wob_cant.solve", "p7_wob_pushed.around", "p7_wob_total", "p7_unev_voice.aloud", "p7_unev_phone.ring", "p7_unev_call.name", "p7_unev_music", "p7_unev_no.ones.vox", "p7_unev_shadows", "p7_unev_total", "p7_unev_check", "p7_exsen_esp.exists", "p7_exsen_esp.exp", "p7_exsen_psychic", "p7_exsen_view.future", "p7_exsen_dream.true", "p7_exsen_dist.msg", "p7_exsen_send.msg", "p7_exsen_total", "p7_exsen_check", "p7_hthk_complex", "p7_hthk_responsblt", "p7_hthk_not.fun", "p7_hthk_lil.challeng", "p7_hthk_avoid.think", "p7_hthk_long.hrs", "p7_hthk_hrd.hav.to", "p7_hthk_smal.daily", "p7_hthk_lil.thought", "p7_hthk_way.to.top", "p7_hthk_new.soltions", "p7_hthk_not.exciting", "p7_hthk_puzzles", "p7_hthk_abstract", "p7_hthk_intel.task", "p7_hthk_mental.effrt", "p7_hthk_job.done", "p7_hthk_not.personal", "p7_hthk_total", "p7_por_thgs.hrt", "p7_por_thgs.hurt_a", "p7_por_thgs.hurt_b", "p7_por_thgs.hurt_c", "p7_por_wifi.thgs", "p7_por_job.wish", "p7_por_angr.cntrl", "p7_por_angr.cntrl_a", "p7_por_angr.cntrl_b", "p7_por_angr.cntrl_c", "p7_por_sprt.envy", "p7_por_read.thgs", "p7_por_read.thgs_a", "p7_por_read.thgs_b", "p7_por_read.thgs_c", "p7_por_stre.spoil", "p7_por_stre.spoil_a", "p7_por_stre.spoil_b", "p7_por_stre.spoil_c", "p7_por_conslt.unseen", "p7_por_mircl.prayer", "p7_por_pry.dead.back", "p7_por_spkn.curse", "p7_por_spkn.curse_a", "p7_por_spkn.curse_b", "p7_por_spkn.curse_c", "p7_por_curse.sick", "p7_por_sprt.put.thgs", "p7_por_fall.in.lov", "p7_por_fall.in.lov_a", "p7_por_fall.in.lov_b", "p7_por_fall.in.lov_c", "p7_por_thgs.heal", "p7_por_visualization", "p7_por_total", "p7_por_check", "p7_mm_ang_feel.hurt", "p7_mm_ang_thgs.hurt", "p7_mm_ang_sprt.hurt", "p7_mm_ang_physical", "p7_mm_ang_sickness", "p7_mm_car_fel.no.pr", "p7_mm_car_thk.no.pr", "p7_mm_car_sprt.help", "p7_mm_car_physical", "p7_mm_car_curing", "p7_mm_env_feel.hurt", "p7_mm_env_thgs.hurt", "p7_mm_env_sprt.hurt", "p7_mm_env_physical", "p7_mm_env_sickness", "p7_mm_thnk.feel.hurt", "p7_mm_sprt.thgs.hurt", "p7_mm_total", "p7_mm_check", "p7_dem_sex", "p7_dem_age", "p7_dem_pocc", "p7_dem_major", "p7_dem_ethnicity", "p7_dem_rur.urb", "p7_dem_affrd.basics", "p7_dem_ses", "p7_dem_how.sprt.relg", "p7_dem_religion", "p7_dem_church", "p7_dem_holy.tung.gif")
NAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercionNAs introduced by coercion
d1 <- d0 %>%
  select(-contains("_dem_"), -ends_with("_cat"), -p7_ctry,
         -contains("total"), -contains("check")) %>%
  gather(question, response, -p7_subj) %>%
  # rescale everything to be in 0, 1
  mutate(scale = gsub("p7_", "", question),
         scale = gsub("_.*$", "", scale),
         response = case_when(
           scale %in% c("abs", "exsen") ~ response,
           scale == "dse" ~ response / 5,
           scale == "hthk" ~ (response + 2) / 4,
           scale %in% c("mm", "unev") ~ response / 3,
           scale == "por" ~ response / 2,
           scale == "se" ~ response / 4,
           scale == "wob" ~ (response + 3) / 6)) %>%
  # get rid of follow-up questions for porosity
  filter(!grepl("_.$", question)) %>%
  select(-scale) %>%
  spread(question, response) %>%
  column_to_rownames("p7_subj")

This is an exploration of dimensionality reduction on Packet 7 (last updated: 2019-05-19). KW thought of this in response to the general question of how (if at all) we can distinguish porosity “beliefs” from spiritual “experiences.”

The basic question here is whether we can distinguish beliefs from experience by tracking patterns of covariance across individual participants: What are the “clusters” of questions that tended to hang together at the participant level (i.e., if someone endorsed one question, what else did they endorse)? If porosity and spiritual experience are fully redundant with each other, we would not expect to see separate clusters of porosity vs. spiritual experience items.

Main take-away: If we retain more than 2-3 factors, we end up distinguishing between porosity and spiritual experience. Two standard methods of determining how many factors to retain (parallel analysis and minimizing BIC) both suggest retaining six or more factors.

Exploratory factor analysis (EFA)

Parallel analysis

fa.parallel(d1)
Parallel analysis suggests that the number of factors =  12  and the number of components =  11 

Parallel analysis suggests retaining 13 factors.

efa13 <- fa(d1, 13, rotate = "varimax")
heatmap_fun(efa13, factor_names = paste0("MR", 1:13))
the condition has length > 1 and only the first element will be usedJoining, by = "capacity"
Joining, by = "factor"
Joining, by = "factor"

efa13_loadings <- efa13$loadings[] %>%
  data.frame() %>%
  rownames_to_column("question") %>%
  gather(factor, loading, -question) %>%
  mutate(scale = gsub("p7_", "", question),
         scale = gsub("_.*$", "", scale),
         factor = factor(factor, levels = paste0("MR", 1:13)))
efa13_loadings_dom <- efa13_loadings %>%
  group_by(scale, question) %>%
  top_n(1, abs(loading)) %>%
  ungroup() %>%
  arrange(factor, desc(abs(loading)))
efa13_loadings_dom %>%
  count(factor, scale) %>%
  complete(factor, scale, fill = list(n = ".")) %>%
  spread(scale, n) %>%
  kable() %>%
  kable_styling()
factor abs dse exsen hthk mm por se unev wob
MR1 . 13 . . . 3 11 . .
MR2 32 . . . . . . . .
MR3 2 . . . . . . . 6
MR4 . . . . 14 3 . . .
MR5 . . . . . . 11 . .
MR6 . . . . . . . . 6
MR7 . 1 . 9 . . . . .
MR8 . . . . . . . 6 .
MR9 . . . 9 . . . . .
MR10 . . . . 3 . . . .
MR11 . . . . . 10 . . .
MR12 . . 7 . . . . . .
MR13 . . . . . . . . .

Quick interpretations:

  • MR1 = spiritual experience I (dse/se)
  • MR2 = absorption
  • MR3 = other personality (wob/abs)
  • MR4 = martha mary
  • MR5 = spiritual experience II (se)
  • MR6 = sense of control (wob)
  • MR7 = need for cognition I (hthk)
  • MR8 = launay-slade (unev)
  • MR9 = need for cognition II (hthk)
  • MR10 = other porosity
  • MR11 = porosity
  • MR12 = sheep-goat (exsen)
  • [MR13 = NULL]

Focus on items that from DSE, SE, Porosity, & MM scales:

efa13_loadings_dom %>% 
  mutate(dom = "bold") %>% 
  select(-loading) %>%
  full_join(efa13_loadings) %>%
  full_join(efa13_loadings_dom %>%
              arrange(desc(factor), abs(loading)) %>%
              mutate(order = 1:nrow(.)) %>%
              select(scale, question, order)) %>%
  mutate(dom = ifelse(is.na(dom), "plain", dom)) %>%
  filter(scale %in% c("dse", "se", "por", "mm")) %>%
  ggplot(aes(x = factor, y = reorder(question, order),
             fill = loading, label = format(round(loading, 2), nsmall =))) +
  facet_grid(scale ~ ., scales = "free", space = "free") +
  geom_tile(color = "black") +
  geom_text(aes(size = dom, fontface = dom)) + 
  scale_fill_distiller("Factor loading", palette = "RdYlBu", limits = c(-1, 1),
                       guide = guide_colorbar(barheight = 20, barwidth = 1)) +
  scale_x_discrete(position = "top") +
  scale_size_manual("Dominant factor?", 
                    values = c(3, 2), labels = c("dominant", "not dominant")) +
  theme_minimal() +
  theme(axis.title = element_blank())
Joining, by = c("question", "factor", "scale")
Joining, by = c("question", "scale")

Minimizing BIC

VSS(d1)

Very Simple Structure
Call: vss(x = x, n = n, rotate = rotate, diagonal = diagonal, fm = fm, 
    n.obs = n.obs, plot = plot, title = title, use = use, cor = cor)
VSS complexity 1 achieves a maximimum of 0.72  with  1  factors
VSS complexity 2 achieves a maximimum of 0.8  with  3  factors

The Velicer MAP achieves a minimum of 0  with  8  factors 
BIC achieves a minimum of  -42163.96  with  6  factors
Sample Size adjusted BIC achieves a minimum of  -12097.56  with  8  factors

Statistics by number of factors 

Minimizing BIC suggests retaining 6 factors.

efa6 <- fa(d1, 6, rotate = "varimax")
heatmap_fun(efa6, factor_names = paste0("MR", 1:6))
the condition has length > 1 and only the first element will be usedJoining, by = "capacity"
Joining, by = "factor"
Joining, by = "factor"

efa6_loadings <- efa6$loadings[] %>%
  data.frame() %>%
  rownames_to_column("question") %>%
  gather(factor, loading, -question) %>%
  mutate(scale = gsub("p7_", "", question),
         scale = gsub("_.*$", "", scale),
         factor = factor(factor, levels = paste0("MR", 1:6)))
efa6_loadings_dom <- efa6_loadings %>%
  group_by(scale, question) %>%
  top_n(1, abs(loading)) %>%
  ungroup() %>%
  arrange(factor, desc(abs(loading)))
efa6_loadings_dom %>%
  count(factor, scale) %>%
  complete(factor, scale, fill = list(n = ".")) %>%
  spread(scale, n) %>%
  kable() %>%
  kable_styling()
factor abs dse exsen hthk mm por se unev wob
MR1 . 14 . . . 1 10 . .
MR2 2 . . 6 . . . . 6
MR3 32 . 4 4 . . . 1 .
MR4 . . 3 . 17 15 . . .
MR5 . . . . . . 12 5 .
MR6 . . . 8 . . . . 6

Quick interpretations:

  • MR1 = spiritual experience I (dse/se)
  • MR2 = other personality (hthk/wob/abs)
  • MR3 = absorption (plus some exsen/hthk/unev)
  • MR4 = porosity (mm/por)
  • MR5 = spiritual experience II (se/unev)
  • MR6 = control (hthk/wob)

Focus on items that from DSE, SE, Porosity, & MM scales:

efa6_loadings_dom %>% 
  mutate(dom = "bold") %>% 
  select(-loading) %>%
  full_join(efa6_loadings) %>%
  full_join(efa6_loadings_dom %>%
              arrange(desc(factor), abs(loading)) %>%
              mutate(order = 1:nrow(.)) %>%
              select(scale, question, order)) %>%
  mutate(dom = ifelse(is.na(dom), "plain", dom)) %>%
  filter(scale %in% c("dse", "se", "por", "mm")) %>%
  ggplot(aes(x = factor, y = reorder(question, order),
             fill = loading, label = format(round(loading, 2), nsmall =))) +
  facet_grid(scale ~ ., scales = "free", space = "free") +
  geom_tile(color = "black") +
  geom_text(aes(size = dom, fontface = dom)) + 
  scale_fill_distiller("Factor loading", palette = "RdYlBu", limits = c(-1, 1),
                       guide = guide_colorbar(barheight = 20, barwidth = 1)) +
  scale_x_discrete(position = "top") +
  scale_size_manual("Dominant factor?", 
                    values = c(3, 2), labels = c("dominant", "not dominant")) +
  theme_minimal() +
  theme(axis.title = element_blank())
Joining, by = c("question", "factor", "scale")
Joining, by = c("question", "scale")

efa6_loadings_dom %>% 
  mutate(dom = "bold") %>% 
  select(-loading) %>%
  full_join(efa6_loadings) %>%
  full_join(efa6_loadings_dom %>%
              arrange(desc(factor), abs(loading)) %>%
              mutate(order = 1:nrow(.)) %>%
              select(scale, question, order)) %>%
  mutate(dom = ifelse(is.na(dom), "plain", dom)) %>%
  # filter(scale %in% c("dse", "se", "por", "mm")) %>%
  ggplot(aes(x = factor, y = reorder(question, order),
             fill = loading, label = format(round(loading, 2), nsmall =))) +
  facet_grid(scale ~ ., scales = "free", space = "free") +
  geom_tile(color = "black") +
  geom_text(aes(size = dom, fontface = dom)) + 
  scale_fill_distiller("Factor loading", palette = "RdYlBu", limits = c(-1, 1),
                       guide = guide_colorbar(barheight = 20, barwidth = 1)) +
  scale_x_discrete(position = "top") +
  scale_size_manual("Dominant factor?", 
                    values = c(3, 2), labels = c("dominant", "not dominant")) +
  theme_minimal() +
  theme(axis.title = element_blank())
Joining, by = c("question", "factor", "scale")
Joining, by = c("question", "scale")

Weisman et al. (2017) retention criteria

reten_fun(d1, "none")
[1] 2

The retention criteria employed in Weisman et al. (2017) suggest retaining 2 factors.

efa2 <- fa(d1, 2, rotate = "varimax")
heatmap_fun(efa2, factor_names = paste0("MR", 1:2))
the condition has length > 1 and only the first element will be usedJoining, by = "capacity"
Joining, by = "factor"
Joining, by = "factor"

efa2_loadings <- efa2$loadings[] %>%
  data.frame() %>%
  rownames_to_column("question") %>%
  gather(factor, loading, -question) %>%
  mutate(scale = gsub("p7_", "", question),
         scale = gsub("_.*$", "", scale),
         factor = factor(factor, levels = paste0("MR", 1:2)))
efa2_loadings_dom <- efa2_loadings %>%
  group_by(scale, question) %>%
  top_n(1, abs(loading)) %>%
  ungroup() %>%
  arrange(factor, desc(abs(loading)))
efa2_loadings_dom %>%
  count(factor, scale) %>%
  complete(factor, scale, fill = list(n = ".")) %>%
  spread(scale, n) %>%
  kable() %>%
  kable_styling()
factor abs dse exsen hthk mm por se unev wob
MR1 2 13 4 8 17 16 19 . 5
MR2 32 1 3 10 . . 3 6 7

Quick interpretations:

  • MR1 = porosity + spiritual experience? (“spiritual”?)
  • MR2 = absorption + personality? (“secular”?)

Focus on items that from DSE, SE, Porosity, & MM scales:

efa2_loadings_dom %>% 
  mutate(dom = "bold") %>% 
  select(-loading) %>%
  full_join(efa2_loadings) %>%
  full_join(efa2_loadings_dom %>%
              arrange(desc(factor), abs(loading)) %>%
              mutate(order = 1:nrow(.)) %>%
              select(scale, question, order)) %>%
  mutate(dom = ifelse(is.na(dom), "plain", dom)) %>%
  filter(scale %in% c("dse", "se", "por", "mm")) %>%
  ggplot(aes(x = factor, y = reorder(question, order),
             fill = loading, label = format(round(loading, 2), nsmall =))) +
  facet_grid(scale ~ ., scales = "free", space = "free") +
  geom_tile(color = "black") +
  geom_text(aes(size = dom, fontface = dom)) + 
  scale_fill_distiller("Factor loading", palette = "RdYlBu", limits = c(-1, 1),
                       guide = guide_colorbar(barheight = 20, barwidth = 1)) +
  scale_x_discrete(position = "top") +
  scale_size_manual("Dominant factor?", 
                    values = c(3, 2), labels = c("dominant", "not dominant")) +
  theme_minimal() +
  theme(axis.title = element_blank())
Joining, by = c("question", "factor", "scale")
Joining, by = c("question", "scale")

Four-factor solution

Just for the hell of it, here’s a four-factor solution.

efa4 <- fa(d1, 4, rotate = "varimax")
heatmap_fun(efa4, factor_names = paste0("MR", 1:4))
the condition has length > 1 and only the first element will be usedJoining, by = "capacity"
Joining, by = "factor"
Joining, by = "factor"

efa4_loadings <- efa4$loadings[] %>%
  data.frame() %>%
  rownames_to_column("question") %>%
  gather(factor, loading, -question) %>%
  mutate(scale = gsub("p7_", "", question),
         scale = gsub("_.*$", "", scale),
         factor = factor(factor, levels = paste0("MR", 1:4)))
efa4_loadings_dom <- efa4_loadings %>%
  group_by(scale, question) %>%
  top_n(1, abs(loading)) %>%
  ungroup() %>%
  arrange(factor, desc(abs(loading)))
efa4_loadings_dom %>%
  count(factor, scale) %>%
  complete(factor, scale, fill = list(n = ".")) %>%
  spread(scale, n) %>%
  kable() %>%
  kable_styling()
factor abs dse exsen hthk mm por se unev wob
MR1 . 14 . 1 . . 19 . .
MR2 33 . 6 4 . . 3 6 .
MR3 1 . . 7 . . . . 9
MR4 . . 1 6 17 16 . . 3

Quick interpretations:

  • MR1 = spiritual experience
  • MR2 = absorption (+ secular experience?)
  • MR3 = controls
  • MR4 = porosity (+ personality?)

Focus on items that from DSE, SE, Porosity, & MM scales:

efa4_loadings_dom %>% 
  mutate(dom = "bold") %>% 
  select(-loading) %>%
  full_join(efa4_loadings) %>%
  full_join(efa4_loadings_dom %>%
              arrange(desc(factor), abs(loading)) %>%
              mutate(order = 1:nrow(.)) %>%
              select(scale, question, order)) %>%
  mutate(dom = ifelse(is.na(dom), "plain", dom)) %>%
  filter(scale %in% c("dse", "se", "por", "mm")) %>%
  ggplot(aes(x = factor, y = reorder(question, order),
             fill = loading, label = format(round(loading, 3), nsmall =))) +
  facet_grid(scale ~ ., scales = "free", space = "free") +
  geom_tile(color = "black") +
  geom_text(aes(size = dom, fontface = dom)) + 
  scale_fill_distiller("Factor loading", palette = "RdYlBu", limits = c(-1, 1),
                       guide = guide_colorbar(barheight = 30, barwidth = 1)) +
  scale_x_discrete(position = "top") +
  scale_size_manual("Dominant factor?", 
                    values = c(3, 2), labels = c("dominant", "not dominant")) +
  theme_minimal() +
  theme(axis.title = element_blank())
Joining, by = c("question", "factor", "scale")
Joining, by = c("question", "scale")

# EXTRA
# hierarchical clustering
clust <- d1 %>% t() %>% dist() %>% hclust()
clust %>%
  ggdendrogram(rotate = T) +
  theme_minimal() +
  theme(panel.grid.major.y = element_blank(),
        panel.grid.minor.y = element_blank()) +
  labs(title = "Hierarchical agglomerative clustering", 
       subtitle = "Complete linkage (default for stats::hclust() function)",
       y = "Height", x = "Question")

LS0tCnRpdGxlOiAiUGFja2V0IDcgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIgpkYXRlOiAiMjAxOS0wNS0xOSIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKLS0tCgpgYGB7ciBnbG9iYWxfb3B0aW9ucywgaW5jbHVkZSA9IEZ9CmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGggPSA0LCBmaWcuYXNwID0gMC42NywKICAgICAgICAgICAgICAgICAgICAgIGluY2x1ZGUgPSBGLCBlY2hvID0gRikKYGBgCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobGFuZ2NvZykKbGlicmFyeShwc3ljaCkKbGlicmFyeShyZWFkeGwpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShsbWU0KQpsaWJyYXJ5KGxtZXJUZXN0KQpsaWJyYXJ5KGthYmxlRXh0cmEpCmxpYnJhcnkobHVicmlkYXRlKQpsaWJyYXJ5KGdnZGVuZHJvKQoKdGhlbWVfc2V0KHRoZW1lX2J3KCkpCmBgYAoKYGBge3J9CmhlYXRtYXBfZnVuIDwtIGZ1bmN0aW9uKGVmYSwgZmFjdG9yX25hbWVzID0gTkEpewogIAogICMgZ2V0IGZhY3RvciBuYW1lcwogIGlmKGlzLm5hKGZhY3Rvcl9uYW1lcykpewogICAgZmFjdG9yX25hbWVzIDwtIHBhc3RlKCJGYWN0b3IiLCAxOmVmYSRmYWN0b3JzKQogIH0KICAKICAjIHB1dCBmYWN0b3JzIGluIGEgc3RhbmRhcmQgb3JkZXIgd2hlbiBhcHBsaWNhYmxlCiAgYm9keV9mYWN0b3JzIDwtIGZhY3Rvcl9uYW1lc1tncmVwbCgiQk9EWSIsIGZhY3Rvcl9uYW1lcyldCiAgCiAgbGVmdG92ZXJzIDwtIGZhY3Rvcl9uYW1lc1shZmFjdG9yX25hbWVzICVpbiUgYm9keV9mYWN0b3JzXQogIGhlYXJ0X2ZhY3RvcnMgPC0gbGVmdG92ZXJzW2dyZXBsKCJIRUFSVCIsIGxlZnRvdmVycyldCiAgCiAgbGVmdG92ZXJzIDwtIGxlZnRvdmVyc1shbGVmdG92ZXJzICVpbiUgaGVhcnRfZmFjdG9yc10KICBtaW5kX2ZhY3RvcnMgPC0gbGVmdG92ZXJzW2dyZXBsKCJNSU5EIiwgbGVmdG92ZXJzKV0KICAKICBvdGhlcl9mYWN0b3JzIDwtIGxlZnRvdmVyc1shbGVmdG92ZXJzICVpbiUgbWluZF9mYWN0b3JzXQogIAogIGZhY3Rvcl9sZXZlbHMgPC0gYyhib2R5X2ZhY3RvcnMsIGhlYXJ0X2ZhY3RvcnMsIG1pbmRfZmFjdG9ycywgb3RoZXJfZmFjdG9ycykKICAKICAjIGdldCBmYWN0b3IgbG9hZGluZ3MKICBsb2FkaW5ncyA8LSBlZmEkbG9hZGluZ3NbXSAlPiUKICAgIGRhdGEuZnJhbWUoKSAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbigiY2FwYWNpdHkiKSAlPiUKICAgIGdhdGhlcihmYWN0b3IsIGxvYWRpbmcsIC1jYXBhY2l0eSkgJT4lCiAgICBtdXRhdGUoZmFjdG9yID0gYXMuY2hhcmFjdGVyKGZhY3RvcihmYWN0b3IsIGxhYmVscyA9IGZhY3Rvcl9uYW1lcykpLAogICAgICAgICAgIGZhY3RvciA9IGZhY3RvcihmYWN0b3IsIGxldmVscyA9IGZhY3Rvcl9sZXZlbHMpKQogIAogICMgZ2V0IGZhLnNvcnQoKSBvcmRlcgogIG9yZGVyIDwtIGxvYWRpbmdzICU+JQogICAgZ3JvdXBfYnkoY2FwYWNpdHkpICU+JQogICAgdG9wX24oMSwgYWJzKGxvYWRpbmcpKSAlPiUKICAgIHVuZ3JvdXAoKSAlPiUKICAgIGFycmFuZ2UoZGVzYyhmYWN0b3IpLCBhYnMobG9hZGluZykpICU+JQogICAgbXV0YXRlKG9yZGVyID0gMTpsZW5ndGgobGV2ZWxzKGZhY3Rvcihsb2FkaW5ncyRjYXBhY2l0eSkpKSkgJT4lCiAgICBzZWxlY3QoY2FwYWNpdHksIG9yZGVyKQogIAogICMgZ2V0IHBlcmNlbnQgc2hhcmVkIHZhcmlhbmNlIGV4cGxhaW5lZAogIHNoYXJlZF92YXIgPC0gZWZhJFZhY2NvdW50ZWQgJT4lCiAgICBkYXRhLmZyYW1lKCkgJT4lCiAgICByb3duYW1lc190b19jb2x1bW4oInN0YXQiKSAlPiUKICAgIGZpbHRlcihzdGF0ID09ICJQcm9wb3J0aW9uIEV4cGxhaW5lZCIpICU+JQogICAgc2VsZWN0KC1zdGF0KSAlPiUKICAgIGdhdGhlcihmYWN0b3IsIHZhcikgJT4lCiAgICBtdXRhdGUoZmFjdG9yID0gYXMuY2hhcmFjdGVyKGZhY3RvcihmYWN0b3IsIGxhYmVscyA9IGZhY3Rvcl9uYW1lcykpLAogICAgICAgICAgIGZhY3RvciA9IGZhY3RvcihmYWN0b3IsIGxldmVscyA9IGZhY3Rvcl9sZXZlbHMpKSAlPiUKICAgIG11dGF0ZSh2YXJfc2hhcmVkID0gcGFzdGUwKGZhY3RvciwgIlxuIiwgcm91bmQodmFyLCAyKSoxMDAsICIlIHNoYXJlZCB2YXIuLCIpKQogIAogICMgZ2V0IHBlcmNlbnQgdG90YWwgdmFyaWFuY2UgZXhwbGFpbmVkCiAgdG90YWxfdmFyIDwtIGVmYSRWYWNjb3VudGVkICU+JQogICAgZGF0YS5mcmFtZSgpICU+JQogICAgcm93bmFtZXNfdG9fY29sdW1uKCJzdGF0IikgJT4lCiAgICBmaWx0ZXIoc3RhdCA9PSAiUHJvcG9ydGlvbiBWYXIiKSAlPiUKICAgIHNlbGVjdCgtc3RhdCkgJT4lCiAgICBnYXRoZXIoZmFjdG9yLCB2YXIpICU+JQogICAgbXV0YXRlKGZhY3RvciA9IGFzLmNoYXJhY3RlcihmYWN0b3IoZmFjdG9yLCBsYWJlbHMgPSBmYWN0b3JfbmFtZXMpKSwKICAgICAgICAgICBmYWN0b3IgPSBmYWN0b3IoZmFjdG9yLCBsZXZlbHMgPSBmYWN0b3JfbGV2ZWxzKSkgJT4lCiAgICBtdXRhdGUodmFyX3RvdGFsID0gcGFzdGUwKHJvdW5kKHZhciwgMikqMTAwLCAiJSB0b3RhbCB2YXIuIikpCiAgCiAgIyBtYWtlIHBsb3QKICBwbG90IDwtIGdncGxvdChsb2FkaW5ncyAlPiUgCiAgICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4ob3JkZXIpICU+JQogICAgICAgICAgICAgICAgICAgbGVmdF9qb2luKHNoYXJlZF92YXIgJT4lIHNlbGVjdCgtdmFyKSkgJT4lCiAgICAgICAgICAgICAgICAgICBsZWZ0X2pvaW4odG90YWxfdmFyICU+JSBzZWxlY3QoLXZhcikpICU+JQogICAgICAgICAgICAgICAgICAgbXV0YXRlKGNhcGFjaXR5ID0gZ3N1YigiXyIsICIgIiwgY2FwYWNpdHkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGZhY3RvciA9IGZhY3RvcihmYWN0b3IsIGxldmVscyA9IGZhY3Rvcl9sZXZlbHMpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHhsYWIgPSBwYXN0ZSh2YXJfc2hhcmVkLCB2YXJfdG90YWwsIHNlcCA9ICJcbiIpKSwKICAgICAgICAgICAgICAgICBhZXMoeCA9IHJlb3JkZXIoeGxhYiwgYXMubnVtZXJpYyhmYWN0b3IpKSwgCiAgICAgICAgICAgICAgICAgICAgIHkgPSByZW9yZGVyKGNhcGFjaXR5LCBvcmRlciksIAogICAgICAgICAgICAgICAgICAgICBmaWxsID0gbG9hZGluZywgCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKGxvYWRpbmcsIDIpLCBuc21hbGwgPSAyKSkpICsKICAgIGdlb21fdGlsZShjb2xvciA9ICJibGFjayIpICsKICAgIGdlb21fdGV4dChzaXplID0gMykgKwogICAgc2NhbGVfZmlsbF9kaXN0aWxsZXIobGltaXRzID0gYygtMSwgMSksIAogICAgICAgICAgICAgICAgICAgICAgICAgcGFsZXR0ZSA9ICJSZFlsQnUiLAogICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9jb2xvcmJhcihiYXJoZWlnaHQgPSAxMCkpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBzY2FsZV94X2Rpc2NyZXRlKHBvc2l0aW9uID0gInRvcCIpICsKICAgIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgCiAgcmV0dXJuKHBsb3QpCiAgCn0KCnNfbW9tZW50cyA8LSBmdW5jdGlvbihwKSB7cCoocCsxKS8yfQpwYXJhbV9lc3QgPC0gZnVuY3Rpb24ocCwgaykge3AqayArIHAgLSAoayooay0xKS8yKX0KY2hlY2tfb2sgPC0gZnVuY3Rpb24ocCwgaykgewogIGEgPC0gKHAtayleMgogIGIgPC0gcCtrCiAgcmV0dXJuKGlmZWxzZShhPmIsIFRSVUUsIEZBTFNFKSkKfQptYXhfb2sgPC0gZnVuY3Rpb24ocCkgewogIGRmX2NoZWNrIDwtIGRhdGEuZnJhbWUoKQogIGZvcihpIGluIDE6cCl7CiAgICBkZl9jaGVja1tpLCJjaGVjayJdIDwtIGNoZWNrX29rKHAsaSkKICB9CiAgbWF4IDwtIGRmX2NoZWNrICU+JSBmaWx0ZXIoY2hlY2spICU+JSBucm93KCkKICByZXR1cm4obWF4KQp9CnJldGVuX2Z1biA8LSBmdW5jdGlvbihkZiwgcm90X3R5cGUgPSBjKCJvYmxpbWluIiwgInZhcmltYXgiLCAibm9uZSIpKXsKICAKICAjIGZpZ3VyZSBvdXQgbWF4IG51bWJlciBvZiBmYWN0b3JzIHRvIHJldGFpbgogIG5fdmFyIDwtIGxlbmd0aChuYW1lcyhkZikpCiAgbWF4X2sgPC0gbWF4X29rKG5fdmFyKQogIAogICMgcnVuIGVmYSB3aXRoIG1heCBmYWN0b3JzLCB1bnJvdGF0ZWQKICBmYV91bnJvdCA8LSBmYShkZiwgbmZhY3RvcnMgPSBtYXhfaywgcm90YXRlID0gIm5vbmUiLCAKICAgICAgICAgICAgICAgICBzY29yZXMgPSAidGVuQmVyZ2UiLCBpbXB1dGUgPSAibWVkaWFuIikKICBlaWdlbiA8LSBmYV91bnJvdCRWYWNjb3VudGVkICU+JQogICAgZGF0YS5mcmFtZSgpICU+JQogICAgcm93bmFtZXNfdG9fY29sdW1uKCJwYXJhbSIpICU+JQogICAgZ2F0aGVyKGZhY3RvciwgdmFsdWUsIC1wYXJhbSkgJT4lCiAgICBzcHJlYWQocGFyYW0sIHZhbHVlKSAlPiUKICAgIGZpbHRlcihgU1MgbG9hZGluZ3NgID4gMSwgYFByb3BvcnRpb24gRXhwbGFpbmVkYCA+IDAuMDUpCiAgcmV0YWluX2sgPC0gbnJvdyhlaWdlbikKICAKICBmYV9yb3QgPC0gZmEoZGYsIG5mYWN0b3JzID0gcmV0YWluX2ssIHJvdGF0ZSA9IHJvdF90eXBlLAogICAgICAgICAgICAgICBzY29yZXMgPSAidGVuQmVyZ2UiLCBpbXB1dGUgPSAibWVkaWFuIikKICAKICBsb2FkaW5ncyA8LSBmYV9yb3QkbG9hZGluZ3NbXSAlPiUKICAgIGRhdGEuZnJhbWUoKSAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbigiY2FwYWNpdHkiKSAlPiUKICAgIGdhdGhlcihmYWN0b3IsIGxvYWRpbmcsIC1jYXBhY2l0eSkgJT4lCiAgICBncm91cF9ieShjYXBhY2l0eSkgJT4lCiAgICB0b3BfbigxLCBhYnMobG9hZGluZykpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgY291bnQoZmFjdG9yKQogIHJldGFpbl9rX2ZpbmFsIDwtIG5yb3cobG9hZGluZ3MpCiAgCiAgcmV0dXJuKHJldGFpbl9rX2ZpbmFsKQp9Cgpzb3VyY2UoIi4vc2NyaXB0cy9wN19kYXRhX3ByZXAuUiIpCmBgYAoKYGBge3J9CmQxIDwtIGQwICU+JQogIHNlbGVjdCgtY29udGFpbnMoIl9kZW1fIiksIC1lbmRzX3dpdGgoIl9jYXQiKSwgLXA3X2N0cnksCiAgICAgICAgIC1jb250YWlucygidG90YWwiKSwgLWNvbnRhaW5zKCJjaGVjayIpKSAlPiUKICBnYXRoZXIocXVlc3Rpb24sIHJlc3BvbnNlLCAtcDdfc3ViaikgJT4lCiAgIyByZXNjYWxlIGV2ZXJ5dGhpbmcgdG8gYmUgaW4gMCwgMQogIG11dGF0ZShzY2FsZSA9IGdzdWIoInA3XyIsICIiLCBxdWVzdGlvbiksCiAgICAgICAgIHNjYWxlID0gZ3N1YigiXy4qJCIsICIiLCBzY2FsZSksCiAgICAgICAgIHJlc3BvbnNlID0gY2FzZV93aGVuKAogICAgICAgICAgIHNjYWxlICVpbiUgYygiYWJzIiwgImV4c2VuIikgfiByZXNwb25zZSwKICAgICAgICAgICBzY2FsZSA9PSAiZHNlIiB+IHJlc3BvbnNlIC8gNSwKICAgICAgICAgICBzY2FsZSA9PSAiaHRoayIgfiAocmVzcG9uc2UgKyAyKSAvIDQsCiAgICAgICAgICAgc2NhbGUgJWluJSBjKCJtbSIsICJ1bmV2IikgfiByZXNwb25zZSAvIDMsCiAgICAgICAgICAgc2NhbGUgPT0gInBvciIgfiByZXNwb25zZSAvIDIsCiAgICAgICAgICAgc2NhbGUgPT0gInNlIiB+IHJlc3BvbnNlIC8gNCwKICAgICAgICAgICBzY2FsZSA9PSAid29iIiB+IChyZXNwb25zZSArIDMpIC8gNikpICU+JQogICMgZ2V0IHJpZCBvZiBmb2xsb3ctdXAgcXVlc3Rpb25zIGZvciBwb3Jvc2l0eQogIGZpbHRlcighZ3JlcGwoIl8uJCIsIHF1ZXN0aW9uKSkgJT4lCiAgc2VsZWN0KC1zY2FsZSkgJT4lCiAgc3ByZWFkKHF1ZXN0aW9uLCByZXNwb25zZSkgJT4lCiAgY29sdW1uX3RvX3Jvd25hbWVzKCJwN19zdWJqIikKYGBgCgpUaGlzIGlzIGFuIGV4cGxvcmF0aW9uIG9mIGRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBvbiBQYWNrZXQgNyAobGFzdCB1cGRhdGVkOiAyMDE5LTA1LTE5KS4gS1cgdGhvdWdodCBvZiB0aGlzIGluIHJlc3BvbnNlIHRvIHRoZSBnZW5lcmFsIHF1ZXN0aW9uIG9mIGhvdyAoaWYgYXQgYWxsKSB3ZSBjYW4gZGlzdGluZ3Vpc2ggcG9yb3NpdHkgImJlbGllZnMiIGZyb20gc3Bpcml0dWFsICJleHBlcmllbmNlcy4iIAoKVGhlIGJhc2ljIHF1ZXN0aW9uIGhlcmUgaXMgd2hldGhlciB3ZSBjYW4gZGlzdGluZ3Vpc2ggYmVsaWVmcyBmcm9tIGV4cGVyaWVuY2UgYnkgdHJhY2tpbmcgcGF0dGVybnMgb2YgY292YXJpYW5jZSBhY3Jvc3MgaW5kaXZpZHVhbCBwYXJ0aWNpcGFudHM6IFdoYXQgYXJlIHRoZSAiY2x1c3RlcnMiIG9mIHF1ZXN0aW9ucyB0aGF0IHRlbmRlZCB0byBoYW5nIHRvZ2V0aGVyIGF0IHRoZSBwYXJ0aWNpcGFudCBsZXZlbCAoaS5lLiwgaWYgc29tZW9uZSBlbmRvcnNlZCBvbmUgcXVlc3Rpb24sIHdoYXQgZWxzZSBkaWQgdGhleSBlbmRvcnNlKT8gSWYgcG9yb3NpdHkgYW5kIHNwaXJpdHVhbCBleHBlcmllbmNlIGFyZSBmdWxseSByZWR1bmRhbnQgd2l0aCBlYWNoIG90aGVyLCB3ZSB3b3VsZCBfbm90XyBleHBlY3QgdG8gc2VlIHNlcGFyYXRlIGNsdXN0ZXJzIG9mIHBvcm9zaXR5IHZzLiBzcGlyaXR1YWwgZXhwZXJpZW5jZSBpdGVtcy4KCioqTWFpbiB0YWtlLWF3YXkqKjogSWYgd2UgcmV0YWluIG1vcmUgdGhhbiAyLTMgZmFjdG9ycywgd2UgZW5kIHVwIGRpc3Rpbmd1aXNoaW5nIGJldHdlZW4gcG9yb3NpdHkgYW5kIHNwaXJpdHVhbCBleHBlcmllbmNlLiBUd28gc3RhbmRhcmQgbWV0aG9kcyBvZiBkZXRlcm1pbmluZyBob3cgbWFueSBmYWN0b3JzIHRvIHJldGFpbiAocGFyYWxsZWwgYW5hbHlzaXMgYW5kIG1pbmltaXppbmcgQklDKSBib3RoIHN1Z2dlc3QgcmV0YWluaW5nIHNpeCBvciBtb3JlIGZhY3RvcnMuIAoKCiMgRXhwbG9yYXRvcnkgZmFjdG9yIGFuYWx5c2lzIChFRkEpCgojIyBQYXJhbGxlbCBhbmFseXNpcwoKYGBge3J9CmZhLnBhcmFsbGVsKGQxKQpgYGAKClBhcmFsbGVsIGFuYWx5c2lzIHN1Z2dlc3RzIHJldGFpbmluZyAxMyBmYWN0b3JzLgoKYGBge3J9CmVmYTEzIDwtIGZhKGQxLCAxMywgcm90YXRlID0gInZhcmltYXgiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuYXNwID0gMiwgaW5jbHVkZSA9IFR9CmhlYXRtYXBfZnVuKGVmYTEzLCBmYWN0b3JfbmFtZXMgPSBwYXN0ZTAoIk1SIiwgMToxMykpCmBgYAoKYGBge3J9CmVmYTEzX2xvYWRpbmdzIDwtIGVmYTEzJGxvYWRpbmdzW10gJT4lCiAgZGF0YS5mcmFtZSgpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigicXVlc3Rpb24iKSAlPiUKICBnYXRoZXIoZmFjdG9yLCBsb2FkaW5nLCAtcXVlc3Rpb24pICU+JQogIG11dGF0ZShzY2FsZSA9IGdzdWIoInA3XyIsICIiLCBxdWVzdGlvbiksCiAgICAgICAgIHNjYWxlID0gZ3N1YigiXy4qJCIsICIiLCBzY2FsZSksCiAgICAgICAgIGZhY3RvciA9IGZhY3RvcihmYWN0b3IsIGxldmVscyA9IHBhc3RlMCgiTVIiLCAxOjEzKSkpCmBgYAoKYGBge3J9CmVmYTEzX2xvYWRpbmdzX2RvbSA8LSBlZmExM19sb2FkaW5ncyAlPiUKICBncm91cF9ieShzY2FsZSwgcXVlc3Rpb24pICU+JQogIHRvcF9uKDEsIGFicyhsb2FkaW5nKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGFycmFuZ2UoZmFjdG9yLCBkZXNjKGFicyhsb2FkaW5nKSkpCmBgYAoKYGBge3IsIGluY2x1ZGUgPSBUfQplZmExM19sb2FkaW5nc19kb20gJT4lCiAgY291bnQoZmFjdG9yLCBzY2FsZSkgJT4lCiAgY29tcGxldGUoZmFjdG9yLCBzY2FsZSwgZmlsbCA9IGxpc3QobiA9ICIuIikpICU+JQogIHNwcmVhZChzY2FsZSwgbikgJT4lCiAga2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKCkKYGBgCgpRdWljayBpbnRlcnByZXRhdGlvbnM6CgotICoqTVIxID0gc3Bpcml0dWFsIGV4cGVyaWVuY2UgSSAoZHNlL3NlKSoqCi0gTVIyID0gYWJzb3JwdGlvbgotIE1SMyA9IG90aGVyIHBlcnNvbmFsaXR5ICh3b2IvYWJzKQotICoqTVI0ID0gbWFydGhhIG1hcnkqKgotICoqTVI1ID0gc3Bpcml0dWFsIGV4cGVyaWVuY2UgSUkgKHNlKSoqCi0gTVI2ID0gc2Vuc2Ugb2YgY29udHJvbCAod29iKQotIE1SNyA9IG5lZWQgZm9yIGNvZ25pdGlvbiBJIChodGhrKQotIE1SOCA9IGxhdW5heS1zbGFkZSAodW5ldikKLSBNUjkgPSBuZWVkIGZvciBjb2duaXRpb24gSUkgKGh0aGspCi0gKipNUjEwID0gb3RoZXIgcG9yb3NpdHkqKgotICoqTVIxMSA9IHBvcm9zaXR5KioKLSBNUjEyID0gc2hlZXAtZ29hdCAoZXhzZW4pCi0gW01SMTMgPSBOVUxMXQoKRm9jdXMgb24gKippdGVtcyoqIHRoYXQgZnJvbSBEU0UsIFNFLCBQb3Jvc2l0eSwgJiBNTSBzY2FsZXM6CgpgYGB7ciwgZmlnLndpZHRoID0gNiwgZmlnLmFzcCA9IDAuOSwgaW5jbHVkZSA9IFR9CmVmYTEzX2xvYWRpbmdzX2RvbSAlPiUgCiAgbXV0YXRlKGRvbSA9ICJib2xkIikgJT4lIAogIHNlbGVjdCgtbG9hZGluZykgJT4lCiAgZnVsbF9qb2luKGVmYTEzX2xvYWRpbmdzKSAlPiUKICBmdWxsX2pvaW4oZWZhMTNfbG9hZGluZ3NfZG9tICU+JQogICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhmYWN0b3IpLCBhYnMobG9hZGluZykpICU+JQogICAgICAgICAgICAgIG11dGF0ZShvcmRlciA9IDE6bnJvdyguKSkgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KHNjYWxlLCBxdWVzdGlvbiwgb3JkZXIpKSAlPiUKICBtdXRhdGUoZG9tID0gaWZlbHNlKGlzLm5hKGRvbSksICJwbGFpbiIsIGRvbSkpICU+JQogIGZpbHRlcihzY2FsZSAlaW4lIGMoImRzZSIsICJzZSIsICJwb3IiLCAibW0iKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yLCB5ID0gcmVvcmRlcihxdWVzdGlvbiwgb3JkZXIpLAogICAgICAgICAgICAgZmlsbCA9IGxvYWRpbmcsIGxhYmVsID0gZm9ybWF0KHJvdW5kKGxvYWRpbmcsIDIpLCBuc21hbGwgPSkpKSArCiAgZmFjZXRfZ3JpZChzY2FsZSB+IC4sIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpICsKICBnZW9tX3RpbGUoY29sb3IgPSAiYmxhY2siKSArCiAgZ2VvbV90ZXh0KGFlcyhzaXplID0gZG9tLCBmb250ZmFjZSA9IGRvbSkpICsgCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIoIkZhY3RvciBsb2FkaW5nIiwgcGFsZXR0ZSA9ICJSZFlsQnUiLCBsaW1pdHMgPSBjKC0xLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2NvbG9yYmFyKGJhcmhlaWdodCA9IDIwLCBiYXJ3aWR0aCA9IDEpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShwb3NpdGlvbiA9ICJ0b3AiKSArCiAgc2NhbGVfc2l6ZV9tYW51YWwoIkRvbWluYW50IGZhY3Rvcj8iLCAKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKDMsIDIpLCBsYWJlbHMgPSBjKCJkb21pbmFudCIsICJub3QgZG9taW5hbnQiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gNiwgZmlnLmFzcCA9IDIsIGluY2x1ZGUgPSBGfQplZmExM19sb2FkaW5nc19kb20gJT4lIAogIG11dGF0ZShkb20gPSAiYm9sZCIpICU+JSAKICBzZWxlY3QoLWxvYWRpbmcpICU+JQogIGZ1bGxfam9pbihlZmExM19sb2FkaW5ncykgJT4lCiAgZnVsbF9qb2luKGVmYTEzX2xvYWRpbmdzX2RvbSAlPiUKICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoZmFjdG9yKSwgYWJzKGxvYWRpbmcpKSAlPiUKICAgICAgICAgICAgICBtdXRhdGUob3JkZXIgPSAxOm5yb3coLikpICU+JQogICAgICAgICAgICAgIHNlbGVjdChzY2FsZSwgcXVlc3Rpb24sIG9yZGVyKSkgJT4lCiAgbXV0YXRlKGRvbSA9IGlmZWxzZShpcy5uYShkb20pLCAicGxhaW4iLCBkb20pKSAlPiUKICAjIGZpbHRlcihzY2FsZSAlaW4lIGMoImRzZSIsICJzZSIsICJwb3IiLCAibW0iKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yLCB5ID0gcmVvcmRlcihxdWVzdGlvbiwgb3JkZXIpLAogICAgICAgICAgICAgZmlsbCA9IGxvYWRpbmcsIGxhYmVsID0gZm9ybWF0KHJvdW5kKGxvYWRpbmcsIDIpLCBuc21hbGwgPSkpKSArCiAgZmFjZXRfZ3JpZChzY2FsZSB+IC4sIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpICsKICBnZW9tX3RpbGUoY29sb3IgPSAiYmxhY2siKSArCiAgZ2VvbV90ZXh0KGFlcyhzaXplID0gZG9tLCBmb250ZmFjZSA9IGRvbSkpICsgCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIoIkZhY3RvciBsb2FkaW5nIiwgcGFsZXR0ZSA9ICJSZFlsQnUiLCBsaW1pdHMgPSBjKC0xLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2NvbG9yYmFyKGJhcmhlaWdodCA9IDIwLCBiYXJ3aWR0aCA9IDEpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShwb3NpdGlvbiA9ICJ0b3AiKSArCiAgc2NhbGVfc2l6ZV9tYW51YWwoIkRvbWluYW50IGZhY3Rvcj8iLCAKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKDMsIDIpLCBsYWJlbHMgPSBjKCJkb21pbmFudCIsICJub3QgZG9taW5hbnQiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgoKIyMgTWluaW1pemluZyBCSUMKCmBgYHtyfQpWU1MoZDEpCmBgYAoKTWluaW1pemluZyBCSUMgc3VnZ2VzdHMgcmV0YWluaW5nIDYgZmFjdG9ycy4KCmBgYHtyfQplZmE2IDwtIGZhKGQxLCA2LCByb3RhdGUgPSAidmFyaW1heCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDQsIGZpZy5hc3AgPSAzLCBpbmNsdWRlID0gVH0KaGVhdG1hcF9mdW4oZWZhNiwgZmFjdG9yX25hbWVzID0gcGFzdGUwKCJNUiIsIDE6NikpCmBgYAoKYGBge3J9CmVmYTZfbG9hZGluZ3MgPC0gZWZhNiRsb2FkaW5nc1tdICU+JQogIGRhdGEuZnJhbWUoKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oInF1ZXN0aW9uIikgJT4lCiAgZ2F0aGVyKGZhY3RvciwgbG9hZGluZywgLXF1ZXN0aW9uKSAlPiUKICBtdXRhdGUoc2NhbGUgPSBnc3ViKCJwN18iLCAiIiwgcXVlc3Rpb24pLAogICAgICAgICBzY2FsZSA9IGdzdWIoIl8uKiQiLCAiIiwgc2NhbGUpLAogICAgICAgICBmYWN0b3IgPSBmYWN0b3IoZmFjdG9yLCBsZXZlbHMgPSBwYXN0ZTAoIk1SIiwgMTo2KSkpCmBgYAoKYGBge3J9CmVmYTZfbG9hZGluZ3NfZG9tIDwtIGVmYTZfbG9hZGluZ3MgJT4lCiAgZ3JvdXBfYnkoc2NhbGUsIHF1ZXN0aW9uKSAlPiUKICB0b3BfbigxLCBhYnMobG9hZGluZykpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGZhY3RvciwgZGVzYyhhYnMobG9hZGluZykpKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gVH0KZWZhNl9sb2FkaW5nc19kb20gJT4lCiAgY291bnQoZmFjdG9yLCBzY2FsZSkgJT4lCiAgY29tcGxldGUoZmFjdG9yLCBzY2FsZSwgZmlsbCA9IGxpc3QobiA9ICIuIikpICU+JQogIHNwcmVhZChzY2FsZSwgbikgJT4lCiAga2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKCkKYGBgCgpRdWljayBpbnRlcnByZXRhdGlvbnM6CgotICoqTVIxID0gc3Bpcml0dWFsIGV4cGVyaWVuY2UgSSAoZHNlL3NlKSoqCi0gTVIyID0gb3RoZXIgcGVyc29uYWxpdHkgKGh0aGsvd29iL2FicykKLSBNUjMgPSBhYnNvcnB0aW9uIChwbHVzIHNvbWUgZXhzZW4vaHRoay91bmV2KQotICoqTVI0ID0gcG9yb3NpdHkgKG1tL3BvcikqKgotICoqTVI1ID0gc3Bpcml0dWFsIGV4cGVyaWVuY2UgSUkgKHNlL3VuZXYpKioKLSBNUjYgPSBjb250cm9sIChodGhrL3dvYikKCkZvY3VzIG9uICoqaXRlbXMqKiB0aGF0IGZyb20gRFNFLCBTRSwgUG9yb3NpdHksICYgTU0gc2NhbGVzOgoKYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5hc3AgPSAwLjksIGluY2x1ZGUgPSBUfQplZmE2X2xvYWRpbmdzX2RvbSAlPiUgCiAgbXV0YXRlKGRvbSA9ICJib2xkIikgJT4lIAogIHNlbGVjdCgtbG9hZGluZykgJT4lCiAgZnVsbF9qb2luKGVmYTZfbG9hZGluZ3MpICU+JQogIGZ1bGxfam9pbihlZmE2X2xvYWRpbmdzX2RvbSAlPiUKICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoZmFjdG9yKSwgYWJzKGxvYWRpbmcpKSAlPiUKICAgICAgICAgICAgICBtdXRhdGUob3JkZXIgPSAxOm5yb3coLikpICU+JQogICAgICAgICAgICAgIHNlbGVjdChzY2FsZSwgcXVlc3Rpb24sIG9yZGVyKSkgJT4lCiAgbXV0YXRlKGRvbSA9IGlmZWxzZShpcy5uYShkb20pLCAicGxhaW4iLCBkb20pKSAlPiUKICBmaWx0ZXIoc2NhbGUgJWluJSBjKCJkc2UiLCAic2UiLCAicG9yIiwgIm1tIikpICU+JQogIGdncGxvdChhZXMoeCA9IGZhY3RvciwgeSA9IHJlb3JkZXIocXVlc3Rpb24sIG9yZGVyKSwKICAgICAgICAgICAgIGZpbGwgPSBsb2FkaW5nLCBsYWJlbCA9IGZvcm1hdChyb3VuZChsb2FkaW5nLCAyKSwgbnNtYWxsID0pKSkgKwogIGZhY2V0X2dyaWQoc2NhbGUgfiAuLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiKSArCiAgZ2VvbV90aWxlKGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChhZXMoc2l6ZSA9IGRvbSwgZm9udGZhY2UgPSBkb20pKSArIAogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKCJGYWN0b3IgbG9hZGluZyIsIHBhbGV0dGUgPSAiUmRZbEJ1IiwgbGltaXRzID0gYygtMSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9jb2xvcmJhcihiYXJoZWlnaHQgPSAyMCwgYmFyd2lkdGggPSAxKSkgKwogIHNjYWxlX3hfZGlzY3JldGUocG9zaXRpb24gPSAidG9wIikgKwogIHNjYWxlX3NpemVfbWFudWFsKCJEb21pbmFudCBmYWN0b3I/IiwgCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygzLCAyKSwgbGFiZWxzID0gYygiZG9taW5hbnQiLCAibm90IGRvbWluYW50IikpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5hc3AgPSAyLCBpbmNsdWRlID0gVH0KZWZhNl9sb2FkaW5nc19kb20gJT4lIAogIG11dGF0ZShkb20gPSAiYm9sZCIpICU+JSAKICBzZWxlY3QoLWxvYWRpbmcpICU+JQogIGZ1bGxfam9pbihlZmE2X2xvYWRpbmdzKSAlPiUKICBmdWxsX2pvaW4oZWZhNl9sb2FkaW5nc19kb20gJT4lCiAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKGZhY3RvciksIGFicyhsb2FkaW5nKSkgJT4lCiAgICAgICAgICAgICAgbXV0YXRlKG9yZGVyID0gMTpucm93KC4pKSAlPiUKICAgICAgICAgICAgICBzZWxlY3Qoc2NhbGUsIHF1ZXN0aW9uLCBvcmRlcikpICU+JQogIG11dGF0ZShkb20gPSBpZmVsc2UoaXMubmEoZG9tKSwgInBsYWluIiwgZG9tKSkgJT4lCiAgIyBmaWx0ZXIoc2NhbGUgJWluJSBjKCJkc2UiLCAic2UiLCAicG9yIiwgIm1tIikpICU+JQogIGdncGxvdChhZXMoeCA9IGZhY3RvciwgeSA9IHJlb3JkZXIocXVlc3Rpb24sIG9yZGVyKSwKICAgICAgICAgICAgIGZpbGwgPSBsb2FkaW5nLCBsYWJlbCA9IGZvcm1hdChyb3VuZChsb2FkaW5nLCAyKSwgbnNtYWxsID0pKSkgKwogIGZhY2V0X2dyaWQoc2NhbGUgfiAuLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiKSArCiAgZ2VvbV90aWxlKGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChhZXMoc2l6ZSA9IGRvbSwgZm9udGZhY2UgPSBkb20pKSArIAogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKCJGYWN0b3IgbG9hZGluZyIsIHBhbGV0dGUgPSAiUmRZbEJ1IiwgbGltaXRzID0gYygtMSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9jb2xvcmJhcihiYXJoZWlnaHQgPSAyMCwgYmFyd2lkdGggPSAxKSkgKwogIHNjYWxlX3hfZGlzY3JldGUocG9zaXRpb24gPSAidG9wIikgKwogIHNjYWxlX3NpemVfbWFudWFsKCJEb21pbmFudCBmYWN0b3I/IiwgCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygzLCAyKSwgbGFiZWxzID0gYygiZG9taW5hbnQiLCAibm90IGRvbWluYW50IikpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMgV2Vpc21hbiBldCBhbC4gKDIwMTcpIHJldGVudGlvbiBjcml0ZXJpYQoKYGBge3J9CnJldGVuX2Z1bihkMSwgIm5vbmUiKQpgYGAKClRoZSByZXRlbnRpb24gY3JpdGVyaWEgZW1wbG95ZWQgaW4gV2Vpc21hbiBldCBhbC4gKDIwMTcpIHN1Z2dlc3QgcmV0YWluaW5nIDIgZmFjdG9ycy4KCmBgYHtyfQplZmEyIDwtIGZhKGQxLCAyLCByb3RhdGUgPSAidmFyaW1heCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDQsIGZpZy5hc3AgPSAzLCBpbmNsdWRlID0gVH0KaGVhdG1hcF9mdW4oZWZhMiwgZmFjdG9yX25hbWVzID0gcGFzdGUwKCJNUiIsIDE6MikpCmBgYAoKYGBge3J9CmVmYTJfbG9hZGluZ3MgPC0gZWZhMiRsb2FkaW5nc1tdICU+JQogIGRhdGEuZnJhbWUoKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oInF1ZXN0aW9uIikgJT4lCiAgZ2F0aGVyKGZhY3RvciwgbG9hZGluZywgLXF1ZXN0aW9uKSAlPiUKICBtdXRhdGUoc2NhbGUgPSBnc3ViKCJwN18iLCAiIiwgcXVlc3Rpb24pLAogICAgICAgICBzY2FsZSA9IGdzdWIoIl8uKiQiLCAiIiwgc2NhbGUpLAogICAgICAgICBmYWN0b3IgPSBmYWN0b3IoZmFjdG9yLCBsZXZlbHMgPSBwYXN0ZTAoIk1SIiwgMToyKSkpCmBgYAoKYGBge3J9CmVmYTJfbG9hZGluZ3NfZG9tIDwtIGVmYTJfbG9hZGluZ3MgJT4lCiAgZ3JvdXBfYnkoc2NhbGUsIHF1ZXN0aW9uKSAlPiUKICB0b3BfbigxLCBhYnMobG9hZGluZykpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGZhY3RvciwgZGVzYyhhYnMobG9hZGluZykpKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gVH0KZWZhMl9sb2FkaW5nc19kb20gJT4lCiAgY291bnQoZmFjdG9yLCBzY2FsZSkgJT4lCiAgY29tcGxldGUoZmFjdG9yLCBzY2FsZSwgZmlsbCA9IGxpc3QobiA9ICIuIikpICU+JQogIHNwcmVhZChzY2FsZSwgbikgJT4lCiAga2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKCkKYGBgCgpRdWljayBpbnRlcnByZXRhdGlvbnM6CgotIE1SMSA9IHBvcm9zaXR5ICsgc3Bpcml0dWFsIGV4cGVyaWVuY2U/ICgic3Bpcml0dWFsIj8pCi0gTVIyID0gYWJzb3JwdGlvbiArIHBlcnNvbmFsaXR5PyAoInNlY3VsYXIiPykKCkZvY3VzIG9uICoqaXRlbXMqKiB0aGF0IGZyb20gRFNFLCBTRSwgUG9yb3NpdHksICYgTU0gc2NhbGVzOgoKYGBge3IsIGZpZy53aWR0aCA9IDMsIGZpZy5hc3AgPSAxLjUsIGluY2x1ZGUgPSBUfQplZmEyX2xvYWRpbmdzX2RvbSAlPiUgCiAgbXV0YXRlKGRvbSA9ICJib2xkIikgJT4lIAogIHNlbGVjdCgtbG9hZGluZykgJT4lCiAgZnVsbF9qb2luKGVmYTJfbG9hZGluZ3MpICU+JQogIGZ1bGxfam9pbihlZmEyX2xvYWRpbmdzX2RvbSAlPiUKICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MoZmFjdG9yKSwgYWJzKGxvYWRpbmcpKSAlPiUKICAgICAgICAgICAgICBtdXRhdGUob3JkZXIgPSAxOm5yb3coLikpICU+JQogICAgICAgICAgICAgIHNlbGVjdChzY2FsZSwgcXVlc3Rpb24sIG9yZGVyKSkgJT4lCiAgbXV0YXRlKGRvbSA9IGlmZWxzZShpcy5uYShkb20pLCAicGxhaW4iLCBkb20pKSAlPiUKICBmaWx0ZXIoc2NhbGUgJWluJSBjKCJkc2UiLCAic2UiLCAicG9yIiwgIm1tIikpICU+JQogIGdncGxvdChhZXMoeCA9IGZhY3RvciwgeSA9IHJlb3JkZXIocXVlc3Rpb24sIG9yZGVyKSwKICAgICAgICAgICAgIGZpbGwgPSBsb2FkaW5nLCBsYWJlbCA9IGZvcm1hdChyb3VuZChsb2FkaW5nLCAyKSwgbnNtYWxsID0pKSkgKwogIGZhY2V0X2dyaWQoc2NhbGUgfiAuLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiKSArCiAgZ2VvbV90aWxlKGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChhZXMoc2l6ZSA9IGRvbSwgZm9udGZhY2UgPSBkb20pKSArIAogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKCJGYWN0b3IgbG9hZGluZyIsIHBhbGV0dGUgPSAiUmRZbEJ1IiwgbGltaXRzID0gYygtMSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9jb2xvcmJhcihiYXJoZWlnaHQgPSAyMCwgYmFyd2lkdGggPSAxKSkgKwogIHNjYWxlX3hfZGlzY3JldGUocG9zaXRpb24gPSAidG9wIikgKwogIHNjYWxlX3NpemVfbWFudWFsKCJEb21pbmFudCBmYWN0b3I/IiwgCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygzLCAyKSwgbGFiZWxzID0gYygiZG9taW5hbnQiLCAibm90IGRvbWluYW50IikpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDMsIGZpZy5hc3AgPSAzLCBpbmNsdWRlID0gRn0KZWZhMl9sb2FkaW5nc19kb20gJT4lIAogIG11dGF0ZShkb20gPSAiYm9sZCIpICU+JSAKICBzZWxlY3QoLWxvYWRpbmcpICU+JQogIGZ1bGxfam9pbihlZmEyX2xvYWRpbmdzKSAlPiUKICBmdWxsX2pvaW4oZWZhMl9sb2FkaW5nc19kb20gJT4lCiAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKGZhY3RvciksIGFicyhsb2FkaW5nKSkgJT4lCiAgICAgICAgICAgICAgbXV0YXRlKG9yZGVyID0gMTpucm93KC4pKSAlPiUKICAgICAgICAgICAgICBzZWxlY3Qoc2NhbGUsIHF1ZXN0aW9uLCBvcmRlcikpICU+JQogIG11dGF0ZShkb20gPSBpZmVsc2UoaXMubmEoZG9tKSwgInBsYWluIiwgZG9tKSkgJT4lCiAgIyBmaWx0ZXIoc2NhbGUgJWluJSBjKCJkc2UiLCAic2UiLCAicG9yIiwgIm1tIikpICU+JQogIGdncGxvdChhZXMoeCA9IGZhY3RvciwgeSA9IHJlb3JkZXIocXVlc3Rpb24sIG9yZGVyKSwKICAgICAgICAgICAgIGZpbGwgPSBsb2FkaW5nLCBsYWJlbCA9IGZvcm1hdChyb3VuZChsb2FkaW5nLCAyKSwgbnNtYWxsID0pKSkgKwogIGZhY2V0X2dyaWQoc2NhbGUgfiAuLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWUiKSArCiAgZ2VvbV90aWxlKGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChhZXMoc2l6ZSA9IGRvbSwgZm9udGZhY2UgPSBkb20pKSArIAogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKCJGYWN0b3IgbG9hZGluZyIsIHBhbGV0dGUgPSAiUmRZbEJ1IiwgbGltaXRzID0gYygtMSwgMSksCiAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9jb2xvcmJhcihiYXJoZWlnaHQgPSAyMCwgYmFyd2lkdGggPSAxKSkgKwogIHNjYWxlX3hfZGlzY3JldGUocG9zaXRpb24gPSAidG9wIikgKwogIHNjYWxlX3NpemVfbWFudWFsKCJEb21pbmFudCBmYWN0b3I/IiwgCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gYygzLCAyKSwgbGFiZWxzID0gYygiZG9taW5hbnQiLCAibm90IGRvbWluYW50IikpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCmBgYAoKIyMgRm91ci1mYWN0b3Igc29sdXRpb24KCkp1c3QgZm9yIHRoZSBoZWxsIG9mIGl0LCBoZXJlJ3MgYSBmb3VyLWZhY3RvciBzb2x1dGlvbi4KCmBgYHtyfQplZmE0IDwtIGZhKGQxLCA0LCByb3RhdGUgPSAidmFyaW1heCIpCmBgYAoKYGBge3IsIGZpZy53aWR0aCA9IDQsIGZpZy5hc3AgPSAzLCBpbmNsdWRlID0gVH0KaGVhdG1hcF9mdW4oZWZhNCwgZmFjdG9yX25hbWVzID0gcGFzdGUwKCJNUiIsIDE6NCkpCmBgYAoKYGBge3J9CmVmYTRfbG9hZGluZ3MgPC0gZWZhNCRsb2FkaW5nc1tdICU+JQogIGRhdGEuZnJhbWUoKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oInF1ZXN0aW9uIikgJT4lCiAgZ2F0aGVyKGZhY3RvciwgbG9hZGluZywgLXF1ZXN0aW9uKSAlPiUKICBtdXRhdGUoc2NhbGUgPSBnc3ViKCJwN18iLCAiIiwgcXVlc3Rpb24pLAogICAgICAgICBzY2FsZSA9IGdzdWIoIl8uKiQiLCAiIiwgc2NhbGUpLAogICAgICAgICBmYWN0b3IgPSBmYWN0b3IoZmFjdG9yLCBsZXZlbHMgPSBwYXN0ZTAoIk1SIiwgMTo0KSkpCmBgYAoKYGBge3J9CmVmYTRfbG9hZGluZ3NfZG9tIDwtIGVmYTRfbG9hZGluZ3MgJT4lCiAgZ3JvdXBfYnkoc2NhbGUsIHF1ZXN0aW9uKSAlPiUKICB0b3BfbigxLCBhYnMobG9hZGluZykpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKGZhY3RvciwgZGVzYyhhYnMobG9hZGluZykpKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gVH0KZWZhNF9sb2FkaW5nc19kb20gJT4lCiAgY291bnQoZmFjdG9yLCBzY2FsZSkgJT4lCiAgY29tcGxldGUoZmFjdG9yLCBzY2FsZSwgZmlsbCA9IGxpc3QobiA9ICIuIikpICU+JQogIHNwcmVhZChzY2FsZSwgbikgJT4lCiAga2FibGUoKSAlPiUKICBrYWJsZV9zdHlsaW5nKCkKYGBgCgpRdWljayBpbnRlcnByZXRhdGlvbnM6CgotIE1SMSA9IHNwaXJpdHVhbCBleHBlcmllbmNlCi0gTVIyID0gYWJzb3JwdGlvbiAoKyBzZWN1bGFyIGV4cGVyaWVuY2U/KQotIE1SMyA9IGNvbnRyb2xzCi0gTVI0ID0gcG9yb3NpdHkgKCsgcGVyc29uYWxpdHk/KQoKRm9jdXMgb24gKippdGVtcyoqIHRoYXQgZnJvbSBEU0UsIFNFLCBQb3Jvc2l0eSwgJiBNTSBzY2FsZXM6CgpgYGB7ciwgZmlnLndpZHRoID0gMywgZmlnLmFzcCA9IDEuOCwgaW5jbHVkZSA9IFR9CmVmYTRfbG9hZGluZ3NfZG9tICU+JSAKICBtdXRhdGUoZG9tID0gImJvbGQiKSAlPiUgCiAgc2VsZWN0KC1sb2FkaW5nKSAlPiUKICBmdWxsX2pvaW4oZWZhNF9sb2FkaW5ncykgJT4lCiAgZnVsbF9qb2luKGVmYTRfbG9hZGluZ3NfZG9tICU+JQogICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhmYWN0b3IpLCBhYnMobG9hZGluZykpICU+JQogICAgICAgICAgICAgIG11dGF0ZShvcmRlciA9IDE6bnJvdyguKSkgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KHNjYWxlLCBxdWVzdGlvbiwgb3JkZXIpKSAlPiUKICBtdXRhdGUoZG9tID0gaWZlbHNlKGlzLm5hKGRvbSksICJwbGFpbiIsIGRvbSkpICU+JQogIGZpbHRlcihzY2FsZSAlaW4lIGMoImRzZSIsICJzZSIsICJwb3IiLCAibW0iKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yLCB5ID0gcmVvcmRlcihxdWVzdGlvbiwgb3JkZXIpLAogICAgICAgICAgICAgZmlsbCA9IGxvYWRpbmcsIGxhYmVsID0gZm9ybWF0KHJvdW5kKGxvYWRpbmcsIDMpLCBuc21hbGwgPSkpKSArCiAgZmFjZXRfZ3JpZChzY2FsZSB+IC4sIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZSIpICsKICBnZW9tX3RpbGUoY29sb3IgPSAiYmxhY2siKSArCiAgZ2VvbV90ZXh0KGFlcyhzaXplID0gZG9tLCBmb250ZmFjZSA9IGRvbSkpICsgCiAgc2NhbGVfZmlsbF9kaXN0aWxsZXIoIkZhY3RvciBsb2FkaW5nIiwgcGFsZXR0ZSA9ICJSZFlsQnUiLCBsaW1pdHMgPSBjKC0xLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICBndWlkZSA9IGd1aWRlX2NvbG9yYmFyKGJhcmhlaWdodCA9IDMwLCBiYXJ3aWR0aCA9IDEpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShwb3NpdGlvbiA9ICJ0b3AiKSArCiAgc2NhbGVfc2l6ZV9tYW51YWwoIkRvbWluYW50IGZhY3Rvcj8iLCAKICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSBjKDMsIDIpLCBsYWJlbHMgPSBjKCJkb21pbmFudCIsICJub3QgZG9taW5hbnQiKSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gMywgZmlnLmFzcCA9IDMuNywgaW5jbHVkZSA9IEZ9CmVmYTRfbG9hZGluZ3NfZG9tICU+JSAKICBtdXRhdGUoZG9tID0gImJvbGQiKSAlPiUgCiAgc2VsZWN0KC1sb2FkaW5nKSAlPiUKICBmdWxsX2pvaW4oZWZhNF9sb2FkaW5ncykgJT4lCiAgZnVsbF9qb2luKGVmYTRfbG9hZGluZ3NfZG9tICU+JQogICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhmYWN0b3IpLCBhYnMobG9hZGluZykpICU+JQogICAgICAgICAgICAgIG11dGF0ZShvcmRlciA9IDE6bnJvdyguKSkgJT4lCiAgICAgICAgICAgICAgc2VsZWN0KHNjYWxlLCBxdWVzdGlvbiwgb3JkZXIpKSAlPiUKICBtdXRhdGUoZG9tID0gaWZlbHNlKGlzLm5hKGRvbSksICJwbGFpbiIsIGRvbSkpICU+JQogICMgZmlsdGVyKHNjYWxlICVpbiUgYygiZHNlIiwgInNlIiwgInBvciIsICJtbSIpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBmYWN0b3IsIHkgPSByZW9yZGVyKHF1ZXN0aW9uLCBvcmRlciksCiAgICAgICAgICAgICBmaWxsID0gbG9hZGluZywgbGFiZWwgPSBmb3JtYXQocm91bmQobG9hZGluZywgMyksIG5zbWFsbCA9KSkpICsKICBmYWNldF9ncmlkKHNjYWxlIH4gLiwgc2NhbGVzID0gImZyZWUiLCBzcGFjZSA9ICJmcmVlIikgKwogIGdlb21fdGlsZShjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3RleHQoYWVzKHNpemUgPSBkb20sIGZvbnRmYWNlID0gZG9tKSkgKyAKICBzY2FsZV9maWxsX2Rpc3RpbGxlcigiRmFjdG9yIGxvYWRpbmciLCBwYWxldHRlID0gIlJkWWxCdSIsIGxpbWl0cyA9IGMoLTEsIDEpLAogICAgICAgICAgICAgICAgICAgICAgIGd1aWRlID0gZ3VpZGVfY29sb3JiYXIoYmFyaGVpZ2h0ID0gMzAsIGJhcndpZHRoID0gMSkpICsKICBzY2FsZV94X2Rpc2NyZXRlKHBvc2l0aW9uID0gInRvcCIpICsKICBzY2FsZV9zaXplX21hbnVhbCgiRG9taW5hbnQgZmFjdG9yPyIsIAogICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoMywgMiksIGxhYmVscyA9IGMoImRvbWluYW50IiwgIm5vdCBkb21pbmFudCIpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQpgYGAKCgoKYGBge3IsIGZpZy53aWR0aCA9IDMsIGZpZy5hc3AgPSA0fQojIEVYVFJBCiMgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcKY2x1c3QgPC0gZDEgJT4lIHQoKSAlPiUgZGlzdCgpICU+JSBoY2x1c3QoKQoKY2x1c3QgJT4lCiAgZ2dkZW5kcm9ncmFtKHJvdGF0ZSA9IFQpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKHBhbmVsLmdyaWQubWFqb3IueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHRpdGxlID0gIkhpZXJhcmNoaWNhbCBhZ2dsb21lcmF0aXZlIGNsdXN0ZXJpbmciLCAKICAgICAgIHN1YnRpdGxlID0gIkNvbXBsZXRlIGxpbmthZ2UgKGRlZmF1bHQgZm9yIHN0YXRzOjpoY2x1c3QoKSBmdW5jdGlvbikiLAogICAgICAgeSA9ICJIZWlnaHQiLCB4ID0gIlF1ZXN0aW9uIikKYGBgCg==